package com.sf.iisp.bind.biz.impl;

import java.text.ParseException;
import java.util.*;

import javax.annotation.Resource;

import com.sf.iisp.BindingException;
import com.sf.iisp.bind.biz.BindingBiz;
import com.sf.iisp.bind.dao.IBindingDao;
import com.sf.iisp.bind.dao.IUnbindingDao;
import com.sf.iisp.bind.dao.IVirtualNoDao;
import com.sf.iisp.bind.domain.*;
import com.sf.iisp.bind.enums.BindType;
import com.sf.iisp.bind.enums.State;
import com.sf.iisp.bind.helper.ResponseMessage;
import com.sf.iisp.bind.helper.SDTFHelper;
import com.sf.iisp.common.Constant;
import com.sf.iisp.kafka.adapter.IKafkaProducerPoolDelegate;
import com.sf.kafka.check.util.JsonUtil;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;

import com.sf.iisp.util.DateUtils;
import com.sf.novatar.base.dao.IPage;
import com.sf.novatar.base.domain.RequestParams;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class BindingBizImpl implements BindingBiz {

    private static final Logger LOGGER = LoggerFactory.getLogger(BindingBizImpl.class);

    @Autowired
    private IBindingDao bindingDao;

    @Autowired
    private IUnbindingDao unbindingDao;

    @Autowired
    private IVirtualNoDao virtualNoDao;

    @Resource
    private RedisTemplate redisTemplate;

    @Qualifier("producer")
    @Resource
    private IKafkaProducerPoolDelegate delegate;

    @Value("${redisTemplate.binding.key.prefix:binding-}")
    private String bindKeyPrefix = "binding-";

    @Value("${redisTemplate.unbinding.key.prefix:unbinding-}")
    private String unBindKeyProfix = "unbinding-";

    @Value("${default.invalid.hour:48}")
    private Integer defaultInvalidHour;

    @Value("${sdtf.binding.service1.url:58.220.61.26:8078/dataInterface/action.do}")
    private String sdtfBindingUrl1;

    @Value("${sdtf.binding.service2.url:58.220.61.27:8078/dataInterface/action.do}")
    private String sdtfBindingUrl2;

    @Value("${isSendKafka:false}")
    private String isSend;

    /**
     * 绑定号码核心服务
     *
     * @param courierPhone  收派员绑定的号码
     * @param customerPhone 客户绑定的号码
     * @param head          消息头
     * @return
     * @throws BindingException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseMessage bind(String courierPhone, String customerPhone, MessageHead head) throws BindingException {
        //5.先验证 收派员号码 客户号码是否已绑定 是否已绑定有虚拟号
        //BindingRecord br = bindingDao.findBindRecordByPhone(State.SUCCESS.toString(), courierPhone, customerPhone);
        //if (br == null) {
        //6.取出所有虚拟号码信息
        List<VirtualNo> virtualList = virtualNoDao.getVirtualNoList();
        for (int i = 0; i < virtualList.size(); i++) {
            VirtualNo virtualNo = virtualList.get(i);
            String vn;
            if (virtualNo != null) {
                vn = virtualNo.getVirtualNo();
                try {
                    //查询    当前虚拟号 是否与收派员 或者客户号码已有绑定
                    BindingRecord byVirtualNoAndPhone = bindingDao.getByVirtualNoAndPhone(vn, courierPhone, customerPhone);
                    //虚拟号 与收派员 或者客户号码都没有已绑定
                    if (byVirtualNoAndPhone == null) {
                        SDTFHelper helper = new SDTFHelper(head, new String[]{sdtfBindingUrl1, sdtfBindingUrl2});
                        String response = helper.accessBindSvc(courierPhone, customerPhone, vn);
                        LOGGER.info("receive SDTF response content ={}", response);
                        Map respMessage = JsonUtil.readData(response, Map.class);
                        if ("0".equals(respMessage.get("Result"))) {
                            LOGGER.info("收派员号码：{},  客户号码：{} ,绑定,虚拟号：{}  成功 !", courierPhone, customerPhone, vn);
                            return new ResponseMessage(State.SUCCESS.toString(), vn);
                        } else {
                            throw new BindingException((String) respMessage.get("ErrorContent"));
                        }
                    }

                } catch (BindingException e) {
                    LOGGER.error(e.getMessage(), e);
                    throw e;
                }
            }
        }

        throw new BindingException(State.OTHER.toString(), "暂无虚拟号码可用");
    }

    @Override
    public String bind(String number, String number1, long time)
            throws BindingException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Map<String, String> bind(Map<String, String> numbers)
            throws BindingException {
        // TODO Auto-generated method stub
        return null;
    }


    @Override
    public String getVirtualNo(String courierPhone, String customerPhone, String virtualNo)
            throws BindingException {
        com.sf.iisp.bind.domain.BindingRecord br = bindingDao.getByVirtualNoAndPhone(virtualNo, courierPhone, customerPhone);
        if (br != null && br.getVirtualNo() != null) {
            return br.getVirtualNo();
        }
        return null;
    }

    @Override
    public String bind(String courierPhone, String customerPhone,
                       String virtualNo) throws BindingException {
        return null;
    }

    @Override
    @Deprecated
    public String bind(ReceiveBindingInfo rb)
            throws BindingException {
        BindingRecord br = setBindRecord(rb);
        return bind(br);
    }

    private String bind(BindingRecord br) {
        ReturnBindingInfo returnInfo = setReturnBindInfo(br);

        //预约时间  如果预约时间为0
        String reservedTime = br.getReservedTime();
        String invalidTime = br.getInvalidTime();
        if (Constant.RESERVED_TIME.equals(reservedTime) || StringUtils.isBlank(reservedTime)) {
            return nowBind(br, returnInfo, invalidTime);
        } else if (checkTime(reservedTime) && checkTime(invalidTime)) {
            return reserveBind(br, returnInfo, reservedTime, invalidTime);
        } else {
            return reserveBind(br, returnInfo, reservedTime, null);
        }
    }

    private String nowBind(BindingRecord br, ReturnBindingInfo returnInfo, String invalidTime) {
        //失效时间
        Date invalidDateTime = null;
        //invalidTime 等于0 默认推迟48小时失效
        if (Constant.INVALID_TIME.equals(invalidTime) || StringUtils.isBlank(invalidTime)) {
            invalidDateTime = DateUtils.dateAddHour(new Date(), defaultInvalidHour);
        } else {
            invalidDateTime = DateUtils.str2Date(invalidTime, DateUtils.datetimeFormat);
        }

        br.setInvalidTime(DateUtils.date2Str(invalidDateTime, DateUtils.datetimeFormat));
        String invalid = DateFormatUtils.format(invalidDateTime, "yyyyMMddHHmm");
        LOGGER.info("set redis item key = {}, value = {}", unBindKeyProfix + invalid, br);
        //TODO:按失效时间解绑，存入redis
        redisTemplate.opsForList().leftPush(unBindKeyProfix + invalid, br);
        //创建解绑记录（４８小时）
        saveUnbindingRecord(br);

        String msg = null;
        ResponseMessage resp = null;
        try {
            resp = bind(br.getCourierPhone(), br.getCustomerPhone(), br);
            br.setState(resp.getState());
            returnInfo.setState(resp.getState());
            returnInfo.setMsg(State.SUCCESS.getMsg());
            returnInfo.setVirtualNo(resp.getMsg());
        } catch (BindingException e) {
            br.setState(e.getErrorCode());
            returnInfo.setState(e.getErrorCode());
            returnInfo.setMsg(e.getMessage());
        } finally {
            msg = JsonUtil.writeValueAsString(returnInfo);
            sendBindResultMsg(msg);

            br.setVirtualNo(resp.getMsg());
            br.setBindTime(new Date());
            bindingDao.updateSaveBindRecord(br);
        }

        return msg;
    }

    private void saveUnbindingRecord(BindingRecord br) {
        UnbindingRecord ubr = new UnbindingRecord();
        ubr.setTransId(UUID.randomUUID().toString());
        ubr.setSysCode(br.getSysCode());
        ubr.setState(State.RESERVED.toString());
        ubr.setVirtualNo(br.getVirtualNo());
        ubr.setCourierPhone(br.getCourierPhone());
        ubr.setCustomerPhone(br.getCustomerPhone());
        ubr.setExtendField1(br.getExtendField1());
        ubr.setExtendField2(br.getExtendField2());
        ubr.setMsgType(BindType.UNBIND.toString());
        ubr.setTransTimestamp(br.getTransTimestamp());
        ubr.setBindTransId(br.getTransId());
        ubr.setReservedTime(br.getInvalidTime());
        ubr.setOriginTransId(br.getTransId());

        unbindingDao.saveOrUpdateUnbindRecord(ubr);
    }

    private String reserveBind(BindingRecord br, ReturnBindingInfo returnInfo, String reservedTime, String invalidTime) {
        String msg = null;
        try {
            //判断是否预约绑定过。预约过则不再预约处理。
            BindingRecord historyBr = bindingDao.findBindRecordByPhone(State.RESERVED.toString(), br.getCourierPhone(), br.getCustomerPhone());
            if (null != historyBr) {
                returnInfo.setState(State.RESERVED.toString());
                returnInfo.setMsg("号码:" + br.getCourierPhone() + "和" + br.getCustomerPhone() + " 已经在 " + reservedTime + " 预约，无需重复预约");
            } else {
                Date reservedDate = org.apache.commons.lang3.time.DateUtils.parseDate(reservedTime, "yyyy-MM-dd HH:mm:ss");
                Date invalidDate = null;
                if (StringUtils.isBlank(invalidTime) || Constant.INVALID_TIME.equals(invalidTime)) {
                    invalidDate = DateUtils.dateAddHour(new Date(), defaultInvalidHour);
                } else {
                    invalidDate = org.apache.commons.lang3.time.DateUtils.parseDate(invalidTime, "yyyy-MM-dd HH:mm:ss");
                }

                if (invalidDate.getTime() > reservedDate.getTime()) {
                    String reserved = DateFormatUtils.format(reservedDate, "yyyyMMddHHmm");
                    String key = bindKeyPrefix + reserved;
                    LOGGER.info("add reserve  redis item key = {}, value = {}", key, br);
                    redisTemplate.opsForList().leftPush(key, br);

                    String invalid = DateFormatUtils.format(invalidDate, "yyyyMMddHHmm");
                    key = unBindKeyProfix + invalid;
                    LOGGER.info("add invalid redis item key = {}, value = {}", key, br);
                    redisTemplate.opsForList().leftPush(key, br);
                    br.setState(State.RESERVED.toString());

                    br.setReservedTime(reservedTime);
                    br.setInvalidTime(DateFormatUtils.format(invalidDate, "yyyy-MM-dd HH:mm:ss"));
                    bindingDao.updateSaveBindRecord(br);
                    saveUnbindingRecord(br);

                    returnInfo.setState(State.RESERVED.toString());
                    returnInfo.setMsg(State.RESERVED.getMsg());
                } else {
                    returnInfo.setState(State.FAILURE.toString());
                    returnInfo.setMsg("失效时间必须大于预约时间");
                }
            }
        } catch (ParseException e) {
            LOGGER.error(e.getMessage(), e);
            br.setState(State.OTHER.toString());
            returnInfo.setState(State.OTHER.toString());
            returnInfo.setMsg("预约日期格式有误，请使用yyyy-MM-dd HH:mm:ss格式");
            bindingDao.updateSaveBindRecord(br);
        } finally {
            msg = JsonUtil.writeValueAsString(returnInfo);
            //TODO:预约是否需要报文，没有号码的,暂时不需要。需要的时候加上
            LOGGER.info("(Don't sent ) SGS  message = {}", msg);


            return msg;
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String bind(String json) throws BindingException {
        BindingRecord br = JsonUtil.readData(json, BindingRecord.class);
        BindingRecord history = bindingDao.findBindRecordByPhone(State.SUCCESS.toString(), br.getCourierPhone(), br.getCustomerPhone());
        if (null != history) {
            LOGGER.info("收派员号码= {} ,  客户号码 ={} 已绑定. 虚拟号码 ={} .", history.getCourierPhone(), history.getCustomerPhone(), history.getVirtualNo());
            ReturnBindingInfo rb = convertNowBindingRecord2ReturnInfo(br, history);
            rb.setState(State.EXISTS.toString());
            rb.setMsg(State.EXISTS.getMsg());
            rb.setVirtualNo(history.getVirtualNo());
            rb.setOriginTransId(br.getTransId());
            String sendContent = JsonUtil.writeValueAsString(rb);
            sendBindResultMsg(sendContent);
            LOGGER.info("send exists binding record = {}", sendContent);
            return sendContent;
        } else {
            return bind(br);
        }

    }

    /**
     * 返回报文
     *
     * @param msg
     */
    private void sendBindResultMsg(String msg) {
        LOGGER.info("send SGS bind message = {}", msg);
        if ("true".equals(isSend)) {
            delegate.sendString(msg);
        }
    }

    public ReturnBindingInfo setReturnBindInfo(BindingRecord br) {
        return convertBindingRecord2ReturnInfo(br);
    }


    public BindingRecord setBindRecord(ReceiveBindingInfo receivSgsInfo) {
        BindingRecord br = new BindingRecord();
        br.setCourierPhone(receivSgsInfo.getCourierPhone());
        br.setCustomerPhone(receivSgsInfo.getCustomerPhone());
        br.setMsgType(receivSgsInfo.getMsgType());
        br.setExtendField1(receivSgsInfo.getExtendField1());
        br.setExtendField2(receivSgsInfo.getExtendField2());
        br.setSysCode(receivSgsInfo.getSysCode());
        br.setTransId(receivSgsInfo.getTransId());
        br.setTransTimestamp(receivSgsInfo.getTransTimestamp());
        return br;
    }


    @Override
    public IPage<BindingRecord> findPageBindingRecord(RequestParams queryParam) {
        return bindingDao.findPageBindingRecord(queryParam);
    }

    @Override
    public ReturnBindingInfo convertBindingRecord2ReturnInfo(BindingRecord record) {
        ReturnBindingInfo rbInfo = new ReturnBindingInfo();
        rbInfo.setCourierPhone(record.getCourierPhone());
        rbInfo.setCustomerPhone(record.getCustomerPhone());
        rbInfo.setMsgType(record.getMsgType());
        rbInfo.setSysCode(record.getSysCode());
        //TODO:define trans id .
        rbInfo.setTransId(UUID.randomUUID().toString());
        rbInfo.setTransTimestamp(DateUtils.date2Str(new Date(), DateUtils.datetimeFormat));
        rbInfo.setOriginTransId(record.getTransId());
        rbInfo.setExtendField1(record.getExtendField1());
        rbInfo.setExtendField2(record.getExtendField2());
        return rbInfo;
    }

    private ReturnBindingInfo convertNowBindingRecord2ReturnInfo(BindingRecord now, BindingRecord record) {
        ReturnBindingInfo rbInfo = new ReturnBindingInfo();
        rbInfo.setCourierPhone(record.getCourierPhone());
        rbInfo.setCustomerPhone(record.getCustomerPhone());
        rbInfo.setMsgType(now.getMsgType());
        rbInfo.setSysCode(now.getSysCode());
        //TODO:define trans id .
        rbInfo.setTransId(UUID.randomUUID().toString());
        rbInfo.setTransTimestamp(DateUtils.date2Str(new Date(), DateUtils.datetimeFormat));
        rbInfo.setOriginTransId(record.getTransId());
        rbInfo.setExtendField1(now.getExtendField1());
        rbInfo.setExtendField2(now.getExtendField2());
        return rbInfo;
    }

    private boolean checkTime(String time) {
        return StringUtils.isNotBlank(time) && !Constant.RESERVED_TIME.equals(time);
    }

    @Override
    public BindingRecord loadById(String id) {
        return bindingDao.loadById(id);
    }


}









